#include <Geometry/Block.h>
#include <mutex>
#include <thread>
#include <MCWorld.h>
    
vector<BlockVertex> BlockBase::faceVertex = BlockMesh::LoadMesh_Obj("模型/cube.obj");  
Picture BlockBase::blockTerrainPic("textures/terrain.png");
unordered_map<BlockID, unordered_map<uint32_t,Texture2D*>> BlockBase::BlockTexture;


Texture2D* BlockBase::GetSingleTexture(BlockID id)
{
    int row = id/16;
    int col = id%16;
    Picture pic = BlockBase::blockTerrainPic.SplitImage(col*16, row*16, col*16+15, row*16+15);
    return new Texture2D(pic);
}  

void BlockBase::LoadBlocksTexture()
{
    static  enum BlockID SingleTextureID[]={
    RoughStone ,
    Dirt,
    Board,
    Stone,
    Bedrock,
    Sand,
    Gravel,
    IronBlock,
    GoldBlock,
    DiamondBlock,
    EmeraldBlock,
    RedStoneBlock,
    GoldMineral,
    IronMineral,
    CoalMineral,
    };

    for(auto id: SingleTextureID)
    {
       BlockTexture[id] = unordered_map<uint32_t,Texture2D*>();
       BlockTexture[id][faceid::ALL] = GetSingleTexture(id);
    }

    BlockTexture[GrassDirt] =  GrassDirt_Block::m_GetTexture();
    BlockTexture[OakWood] =  OakWood_Block::m_GetTexture();
    
}

unordered_map<Texture2D*, vector<BlockVertex>> BlockBase::GetfaceVandT(int face)
{   
    unordered_map<Texture2D*, vector<BlockVertex>> res;
    unordered_map<uint32_t,Texture2D*> texture;

    if(BlockTexture.find(id) != BlockTexture.end())
        texture = BlockTexture[id];  
    else
    {
        texture = this->GetTexture();
    }
    
 
    
    
    
    if(texture.size() == 1)
    {
        res[texture.begin()->second] = GetfaceV(face);
    }
    else
    {
        for(auto &t: texture)
        {
           
            int T = t.first&face;

            vector<BlockVertex> Vs;

            auto F = [&](int Face)
            {
                auto tmp = GetfaceV(Face);
                
                for(auto V: tmp)
                    Vs.push_back(V);

            };
            if(T&X)F(X);
            if(T&Y)F(Y);
            if(T&Z)F(Z);
            if(T&X_)F(X_);
            if(T&Y_)F(Y_);
            if(T&Z_)F(Z_); 

             res[t.second] = Vs;
        }
    }

    return res;
}

vector<BlockVertex> BlockBase::GetfaceV(int face)
{
   
    vector<BlockVertex> faceV;

    if (face & X) 
    {
        for(int i = 0; i < 6; i++)
        {
            auto tmp = faceVertex[i];
            tmp.brightnessLevel = (this->brightnessLevel[0])/15.f;
            faceV.push_back(tmp);
    
        }  
    }

    if(face&Y)
    {
        for(int i = 6; i < 12; i++)
        {
            auto tmp = faceVertex[i];
            tmp.brightnessLevel = (this->brightnessLevel[1])/15.f;
            faceV.push_back(tmp);
        }
    }


    if(face&Z)
    {
        for(int i = 12; i < 18; i++)
        {
            auto tmp = faceVertex[i];
            tmp.brightnessLevel = (this->brightnessLevel[2])/15.f;
            faceV.push_back(tmp);
        }
    }

    if(face&X_)
    {
        for(int i = 18; i < 24; i++)
        {
            auto tmp = faceVertex[i];
            tmp.brightnessLevel = (this->brightnessLevel[3])/15.f;
            faceV.push_back(tmp);
        }
    }

    if(face&Y_)
    {
        for(int i = 24; i < 30; i++)
        {
            auto tmp = faceVertex[i];
         
            tmp.brightnessLevel = (this->brightnessLevel[4])/15.f;
            faceV.push_back(tmp);
        }
    }

    if(face&Z_)
    {
        for(int i = 30; i < 36; i++)
        {
            auto tmp = faceVertex[i];
            tmp.brightnessLevel = (this->brightnessLevel[5])/15.f;
            faceV.push_back(tmp);
        }
    }

    return faceV;
}









unordered_map<uint32_t,Texture2D*> BlockBase::GetTexture()
{
    return BlockTexture[id];
}

void BlockBase::SetBrightnessLevel_face(int  face, uint8_t brightnessLevel)
{
   if(face & X)
   {
       this->brightnessLevel[0] = brightnessLevel;
   }
   if(face & Y)
   {
       this->brightnessLevel[1] = brightnessLevel;
   }
   if(face & Z)
   {
       this->brightnessLevel[2] = brightnessLevel;
   }
   if(face & X_)
   {
       this->brightnessLevel[3] = brightnessLevel;
   }
   if(face & Y_)
   {
       this->brightnessLevel[4] = brightnessLevel;
   }
   if(face & Z_)
   {
       this->brightnessLevel[5] = brightnessLevel;
   }
}



void BlockBase::DrawBlockline(glm::vec3 pos)
{
    static Shader  * shader = nullptr;
    static uint32_t VAO;
    static uint32_t VBO;
    static uint32_t EBO;

    if(shader == nullptr)
    {
        shader = new Shader("shader/blockLineshader.vs", "shader/blockLineshader.fs");
        shader->InitUboCamera();
        shader->SetProjection(MCWorld::GetCamera().GetProjectionMatrix());
            // First, define the vertices for the block line
        float vertices[] = {
            // 顶点坐标
            0.f,  0.f,  0.f,
            1.f,  0.f,  0.f,
            1.f,  1.f,  0.f,
            0.f,  1.f,  0.f,
            0.f,  0.f,  1.f,
            1.f,  0.f,  1.f,
            1.f,  1.f,  1.f,
            0.f,  1.f,  1.f
        };
        unsigned int indices[] = {
            // 立方体的边框索引
            0, 1, 1, 2, 2, 3, 3, 0,
            4, 5, 5, 6, 6, 7, 7, 4,
            0, 4, 1, 5, 2, 6, 3, 7
        };
        // Generate and bind a Vertex Array Object and a Vertex Buffer Object
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glGenBuffers(1, &EBO);
        // Bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
        glBindVertexArray(VAO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

        // Set the vertex attributes pointers
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);
        
        // Unbind the VAO
        glBindVertexArray(0);
    }
    
    shader->use();
    shader->setMat4("model", glm::translate(glm::mat4(1.0), pos));
    shader->SetView(MCWorld::GetCamera().GetViewMatrix());
    
    // Draw the block line
    glBindVertexArray(VAO);
    
    glDrawElements(GL_LINES, 24, GL_UNSIGNED_INT, 0);
 
    glBindVertexArray(0);

    shader->Unuse();

}

















unordered_map<uint32_t,Texture2D*> OakWood_Block::m_GetTexture()
{

    unordered_map<uint32_t,Texture2D*> res;
    int row = GrassDirt/16;
    int col = GrassDirt%16;
    
    uint32_t faceid = 0B00011011;
    Picture pic1 = BlockBase::blockTerrainPic.SplitImage(col*16, row*16, col*16+15, row*16+15);
    res[faceid] = new Texture2D(pic1.data, pic1.width, pic1.height,GL_RGBA);
    
    uint32_t faceid2 = 0B00100100;
    Picture pic2 = BlockBase::blockTerrainPic.SplitImage(col*16+16, row*16, col*16+15+16, row*16+15);
    res[faceid2] = new Texture2D(pic2.data, pic2.width, pic2.height,GL_RGBA);
    
    return res;
}


unordered_map<uint32_t,Texture2D*> GrassDirt_Block::m_GetTexture() 
{
    unordered_map<uint32_t,Texture2D*> res;
    int row = 0;
    int col = 3;

    uint32_t faceid = 0B00011011 ; 
    Picture pic1 = BlockBase::blockTerrainPic.SplitImage(col*16, row*16, col*16+15, row*16+15);
    res[faceid] = new Texture2D(pic1.data, pic1.width, pic1.height,GL_RGBA);
    
    col = 0;
    faceid = 0B00000100;
    Picture pic2 = BlockBase::blockTerrainPic.SplitImage(col*16, row*16, col*16+15, row*16+15) * glm::vec3(0.7f,1.3f,0.7f);
    res[faceid] = new Texture2D(pic2.data, pic2.width, pic2.height,GL_RGBA);

    col = 2;
    faceid = 0B00100000;
    Picture pic3 = BlockBase::blockTerrainPic.SplitImage(col*16, row*16, col*16+15, row*16+15);
    res[faceid] = new Texture2D(pic2.data, pic2.width, pic2.height,GL_RGBA);

    return res; 
}
